package com.live.video.authority.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.live.video.authority.constant.AuthorityConstant;
import com.live.video.authority.constant.CommonConstant;
import com.live.video.authority.service.JWTService;
import com.live.video.authority.vo.CommonResponseVO;
import com.live.video.authority.vo.JwtToken;
import com.live.video.authority.vo.LoginUserInfo;
import com.live.video.authority.vo.UsernameAndPassword;
import com.live.video.common.IdGenerator;
import com.live.video.common.SaltMD5Util;
import com.live.video.common.SystemConstant;
import com.live.video.web.entity.Students;
import com.live.video.web.mapper.StudentsMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Decoder;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.UUID;

/**
 * @author CHENCHEN
 * @title
 * @createTime 2021-12-1216:22
 */
@Slf4j
@Service
public class JWTServiceImpl implements JWTService {

    @Autowired
    private StudentsMapper studentsMapper;

    @Override
    public CommonResponseVO generatorToken(String username, String passoword) throws Exception {
        return this.generatorToken(username, passoword, 0);
    }

    @Override
    public CommonResponseVO generatorToken(String username, String password, int expire) throws Exception {
        log.info("request get userinfo is : [{}] , [{}]", username, password);
        //如果正确, 这里的id 和 username 为数据库中查询出来的信息
        Students students = studentsMapper.selectStudentByPhoto(username);
        if(ObjectUtil.isEmpty(students)){
            return CommonResponseVO.FAIL(SystemConstant.LOGINERROR,"账号或密码错误");
        }
        //前端传递过来的密码需要是MD5加密后的数据，将数据库中的盐与传递密码进行加盐加密得到的结果与数据库进行匹配
        String validate = SaltMD5Util.getInstance().validate(students.getSalt(), password);
        if(!students.getPassword().equals(validate)){
            return CommonResponseVO.FAIL(SystemConstant.LOGINERROR,"账号或密码错误");
        }

        LoginUserInfo loginUserInfo = new LoginUserInfo((long) students.getId(), students.getCellPhoneNumber());

        if(expire <= 0){
            expire = AuthorityConstant.DEFAULT_EXPIRE_DAY;
        }
        ZonedDateTime zdt = LocalDate.now().plus(expire, ChronoUnit.DAYS).atStartOfDay(ZoneId.systemDefault());
        Date expireDate = Date.from(zdt.toInstant());
        return CommonResponseVO.SUCCESS(new JwtToken(Jwts.builder()
                //jwt payload -> K : V
                .claim(CommonConstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserInfo))
                //jwt id
                .setId(UUID.randomUUID().toString())
                //jwt 过期时间
                .setExpiration(expireDate)
                //jwt 签名 -> 加密
                .signWith(getPrivateKey(), SignatureAlgorithm.RS256)
                //jwt 生成信息
                .compact(),students.getStudentName()));
    }

    @Override
    public CommonResponseVO registerUserAndGeneratorToken(UsernameAndPassword usernameAndPassword, Students students) throws Exception {
        log.info("request get userinfo is : [{}] , [{}]", usernameAndPassword, students);
        Students dbStudent = studentsMapper.selectStudentByPhoto(usernameAndPassword.getUsername());
        if(ObjectUtil.isNotEmpty(dbStudent)){
            log.info("register is fail");
            return CommonResponseVO.FAIL(SystemConstant.REGISTERERROR, "用户名已存在");
        }
        students.setId(IdGenerator.generateId());
        students.setCellPhoneNumber(usernameAndPassword.getUsername());
        Pair<String, String> pair = SaltMD5Util.getInstance().add(usernameAndPassword.getPassword(), 1);
        students.setPassword(pair.getRight());
        students.setSalt(pair.getLeft());
        log.info("student in database content is : [{}]", students);
        int i = studentsMapper.insertSelective(students);
        if(i == 0){
            log.info("register is fail , data is not come in database");
            return CommonResponseVO.FAIL(SystemConstant.SYSTEMERROR, "系统异常");
        }
        //注册成功后
        return generatorToken(usernameAndPassword.getUsername(), usernameAndPassword.getPassword());
    }

    @Override
    public CommonResponseVO resetPassword(String username, String newPassword, String oldPassword) throws Exception {
        Students students = studentsMapper.selectStudentByPhoto(username);
        //前端传递过来的密码需要是MD5加密后的数据，将数据库中的盐与传递密码进行加盐加密得到的结果与数据库进行匹配
        String validate = SaltMD5Util.getInstance().validate(students.getSalt(), oldPassword);
        if(!students.getPassword().equals(validate)){
            return CommonResponseVO.FAIL(SystemConstant.LOGINERROR,"密码错误");
        }
        Pair<String, String> pair = SaltMD5Util.getInstance().add(newPassword, 1);
        students.setPassword(pair.getRight());
        students.setSalt(pair.getLeft());
        int i = studentsMapper.updateByPrimaryKeySelective(students);
        if(i == 0){
            log.info("register is fail , data is not come in database");
            return CommonResponseVO.FAIL(SystemConstant.SYSTEMERROR, "系统异常");
        }
        return CommonResponseVO.SUCCESS;
    }

    private PrivateKey getPrivateKey() throws Exception{
        return KeyFactory
                .getInstance("RSA")
                .generatePrivate(
                        new PKCS8EncodedKeySpec(
                                new BASE64Decoder()
                                        .decodeBuffer(
                                                AuthorityConstant.PRIVATE_KEY
                                        )
                        )
                );
    }
}
